-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local lu = require('luaunit')

-- require所有依赖
local log = require 'mc.logging'
local ctx = require 'mc.context'
local mdb = require 'mc.mdb'
local c_factory = require 'mc.orm.factory'
local sd_bus = require 'sd_bus.org_freedesktop_dbus'
local c_network_adapter = require 'device.class.network_adapter'
local comm_fun = require 'device.class.nic_mgmt.comm_fun'
local comm_defs = require 'device.class.nic_mgmt.comm_defs'
local card_init = require 'device.class.nic_mgmt.card.card_init'

local card_mgmt = require 'device.class.nic_mgmt.card_mgmt'

-- 记录原始实现
local origs = {}
local function save_orig(tbl, key)
    origs[tbl] = origs[tbl] or {}
    origs[tbl][key] = tbl[key]
end
local function restore_all()
    for tbl, keys in pairs(origs) do
        for k, v in pairs(keys) do tbl[k] = v end
    end
end

TestCardMgmt = {}

function TestCardMgmt:setUp()
    -- mock log
    save_orig(log, 'notice')
    save_orig(log, 'error')
    log.notice = function() end
    log.error = function() end
    -- mock comm_fun
    save_orig(comm_fun, 'set_interface_add_signal')
    save_orig(comm_fun, 'get_object_name_by_device_path')
    save_orig(comm_fun, 'get_all_device_paths')
    save_orig(comm_fun, 'get_device_obj_by_path_interface')
    save_orig(comm_fun, 'set_interface_del_signal')
    comm_fun.set_interface_add_signal = function(bus, sig_slot, pattern, iface, cb)
        TestCardMgmt._called_set_interface_add_signal = { bus, sig_slot, pattern, iface, cb }
        cb('/mock/path')
    end
    comm_fun.set_interface_del_signal = function(bus, sig_slot, pattern, iface, cb)
        sig_slot[#sig_slot + 1] = 456
        if not bus.match then
            bus.match = function() return 456 end
        end
    end
    comm_fun.get_object_name_by_device_path = function() return 'obj_name' end
    comm_fun.get_all_device_paths = function() return { '/mock/path' } end
    comm_fun.get_device_obj_by_path_interface = function() return {} end
    -- mock comm_defs
    save_orig(comm_defs, 'CARD_DEVICE_PATH_PATTERN')
    save_orig(comm_defs, 'NETWORK_ADAPTER_DEVICE_INTERFACE')
    save_orig(comm_defs, 'MACA_SERVICE')
    save_orig(comm_defs, 'MDB_PATH')
    save_orig(comm_defs, 'MDB_INTERFACE')
    save_orig(comm_defs, 'PCIE_DEVICE_INTERFACE')
    save_orig(comm_defs, 'PCIE_CARD_DEVICE_INTERFACE')
    comm_defs.CARD_DEVICE_PATH_PATTERN = '/mock/pattern'
    comm_defs.NETWORK_ADAPTER_DEVICE_INTERFACE = 'iface'
    comm_defs.MACA_SERVICE = 'svc'
    comm_defs.MDB_PATH = '/mdb'
    comm_defs.MDB_INTERFACE = 'mdb_iface'
    comm_defs.PCIE_DEVICE_INTERFACE = 'iface1'
    comm_defs.PCIE_CARD_DEVICE_INTERFACE = 'iface2'
    -- mock ctx
    save_orig(ctx, 'get_context_or_default')
    ctx.get_context_or_default = function() return {} end
    -- mock mdb
    save_orig(mdb, 'register_interface')
    save_orig(mdb, 'get_object')
    mdb.register_interface = function() end
    mdb.get_object = function()
        return {
            get_property = function(self, prop)
                if prop == 'DeviceName' then return 0, 'NodeId' end
                return 0, 'val'
            end
        }
    end
    -- mock c_network_adapter
    save_orig(c_network_adapter, 'create_mdb_object')
    save_orig(c_network_adapter, 'collection')
    c_network_adapter.create_mdb_object = function(tbl) return tbl end
    c_network_adapter.collection = {
        find = function(self, q)
            return { children = {}, NetworkPortCount = 1, NodeId = q and q.ID or 'NodeId' }
        end,
        del_object = function() end
    }
    -- mock c_factory
    save_orig(c_factory, 'get_instance')
    c_factory.get_instance = function() return { create_object = function() end } end
    -- mock card_init
    save_orig(card_init, 'init')
    TestCardMgmt._card_init_called = {}
    card_init.init = function(obj) TestCardMgmt._card_init_called.obj = obj end
    -- mock sd_bus
    save_orig(sd_bus, 'ObjMgrPropertiesChanged')
    save_orig(sd_bus, 'MatchRule')
    sd_bus.ObjMgrPropertiesChanged = { name = 'n', interface = 'i' }
    sd_bus.MatchRule = { signal = function() return { with_path = function() return 'sig' end } end }
    -- mock skynet
    local skynet = require 'skynet'
    save_orig(skynet, 'sleep')
    skynet.sleep = function() end
    -- mock client
    local client = require 'network_adapter.client'
    save_orig(client, 'OnComponentPropertiesChanged')
    save_orig(client, 'ForeachComponentObjects')
    save_orig(client, 'GetComponentObjects')
    save_orig(client, 'OnFruCtrlPropertiesChanged')
    client.OnComponentPropertiesChanged = function(self, callback) end
    client.ForeachComponentObjects = function(self, callback) end
    client.GetComponentObjects = function(self) return {} end
    client.OnFruCtrlPropertiesChanged = function(self, callback) end
    -- mock fructl
    local fructl = require 'infrastructure.fructl'
    save_orig(fructl, 'get_power_status')
    fructl.get_power_status = function() return 'ON' end
end

function TestCardMgmt:tearDown()
    restore_all()
end

function TestCardMgmt:test_ctor_and_init()
    local bus = {}
    local mgr = card_mgmt
    mgr.bus = bus
    mgr.objects = {}
    mgr.sig_slot = {}
    lu.assertIs(mgr.bus, bus)
    lu.assertIsTable(mgr.objects)
    lu.assertIsTable(mgr.sig_slot)
end

function TestCardMgmt:test_init_obj_and_register_children()
    local bus = {}
    local mgr = card_mgmt
    mgr.bus = bus
    mgr.objects = {}
    mgr.sig_slot = {}
    -- mock bus:call，确保get_node_id_by_device_path能正常返回
    bus.call = function(self, service, path, interface, method, signature, context, device_path, interfaces)
        return { ['svc'] = { 'iface1' } }
    end
    -- 执行
    mgr:init_obj('/mock/path')
    lu.assertIsTable(mgr.objects['/mock/path'])
    local card_orm_obj = mgr.objects['/mock/path']
    local port_orm_obj = { id = 1 }
    mgr:register_children(port_orm_obj, card_orm_obj)
    lu.assertEquals(card_orm_obj.children[1], port_orm_obj)
    lu.assertIs(TestCardMgmt._card_init_called.obj, card_orm_obj)
end

function TestCardMgmt:test_get_orm_obj_by_device_path()
    local bus = {}
    local mgr = card_mgmt
    mgr.bus = bus
    mgr.objects = { ['/mock/path'] = { id = 1 } }
    local obj = mgr:get_orm_obj_by_device_path('/mock/path')
    lu.assertEquals(obj.id, 1)
    local obj2 = mgr:get_orm_obj_by_device_path('/notfound')
    lu.assertNil(obj2)
end

function TestCardMgmt:test_init()
    local bus = {}
    local mgr = card_mgmt
    mgr.bus = bus
    mgr.objects = {}
    mgr.sig_slot = {}
    -- mock bus:call，确保get_node_id_by_device_path能正常返回
    bus.call = function(self, service, path, interface, method, signature, context, device_path, interfaces)
        return { ['svc'] = { 'iface1' } }
    end
    -- 执行
    mgr:init()
    lu.assertIsTable(mgr.objects['/mock/path'])
end

function TestCardMgmt:test_create_card_orm_object_nodeid_nil()
    local orig_get_object = mdb.get_object
    mdb.get_object = function() return { get_property = function() return 1, nil end } end
    local mgr = card_mgmt
    mgr.bus = {}
    mgr.objects = {}
    mgr.sig_slot = {}
    mgr:init_obj('/mock/path')
    lu.assertNil(mgr.objects['/mock/path'])
    mdb.get_object = orig_get_object
end

function TestCardMgmt:test_create_card_orm_object_objectname_nil()
    local orig_get_object_name = comm_fun.get_object_name_by_device_path
    comm_fun.get_object_name_by_device_path = function() return nil end
    local mgr = card_mgmt
    mgr.bus = {}
    mgr.objects = {}
    mgr.sig_slot = {}
    mgr:init_obj('/mock/path')
    lu.assertNil(mgr.objects['/mock/path'])
    comm_fun.get_object_name_by_device_path = orig_get_object_name
end

function TestCardMgmt:test_init_obj_already_exists()
    local bus = {}
    local mgr = card_mgmt
    mgr.bus = bus
    mgr.objects = { ['/mock/path'] = { id = 1 } }
    mgr.sig_slot = {}
    mgr:init_obj('/mock/path')
    lu.assertEquals(mgr.objects['/mock/path'].id, 1)
end

function TestCardMgmt:test_get_orm_obj_by_device_path_log_error()
    local log_err_called = false
    local orig_error = log.error
    log.error = function() log_err_called = true end
    local bus = {}
    local mgr = card_mgmt
    mgr.bus = bus
    mgr.objects = {}
    local obj = mgr:get_orm_obj_by_device_path('/notfound')
    lu.assertNil(obj)
    lu.assertTrue(log_err_called)
    log.error = orig_error
end

function TestCardMgmt:test_firmware_version_sync_to_ports()
    local port1 = { FirmwareVersion = "old_version_1" }
    local port2 = { FirmwareVersion = "old_version_2" }
    local orm_obj = {
        FirmwareVersion = "old_version",
        children = { port1, port2 }
    }

    local captured_callback = nil
    local mock_bus = {
        match = function(self, signal, callback)
            captured_callback = callback
            return {}
        end,
        call = function(self, service, path, interface, method, signature, context, device_path, interfaces)
            return { ['svc'] = { 'iface1' } }
        end
    }

    local original_create_card_orm_object = c_network_adapter.collection.find
    c_network_adapter.collection.find = function(self, q)
        return orm_obj
    end

    local mgr = card_mgmt
    mgr.bus = mock_bus
    mgr.objects = {}
    mgr.sig_slot = {}

    local mock_msg = {
        read = function(self, pattern)
            local interface = comm_defs.NETWORK_ADAPTER_DEVICE_INTERFACE
            local props = {
                FirmwareVersion = {
                    value = function() return "new_version_1.2.3" end
                }
            }
            return interface, props
        end
    }

    mgr:init_obj('/mock/path')

    lu.assertNotNil(captured_callback, "Should capture property change callback")

    captured_callback(mock_msg)

    lu.assertEquals(orm_obj.FirmwareVersion, "new_version_1.2.3")
    lu.assertEquals(port1.FirmwareVersion, "new_version_1.2.3")
    lu.assertEquals(port2.FirmwareVersion, "new_version_1.2.3")

    c_network_adapter.collection.find = original_create_card_orm_object
end

local function create_mock_device_obj(ospower_state)
    return { OSPowerState = ospower_state or 0 }
end

local function create_mock_bus()
    return {
        call = function() return { ['/test/path'] = { 'bmc.dev.PCIeDevice' } } end,
        match = function() return function() end end
    }
end

local function create_mock_mdb_object()
    return {
        get_property = function(self, prop_name)
            if prop_name == 'DeviceName' then
                return 0, 'test_device_name'
            end
            return -1, nil
        end
    }
end

local function setup_log_capture(log_calls)
    log.notice = function(_, fmt, ...)
        local msg = string.format(fmt, ...)
        table.insert(log_calls, msg)
    end
    log.error = function(_, fmt, ...)
        local msg = string.format(fmt, ...)
        table.insert(log_calls, msg)
    end
    log.warning = function(_, fmt, ...)
        local msg = string.format(fmt, ...)
        table.insert(log_calls, msg)
    end
end

local function setup_basic_mocks()
    comm_fun.get_object_name_by_device_path = function() return 'test_obj' end
    c_network_adapter.create_mdb_object = function() return { children = {} } end
    mdb.get_object = create_mock_mdb_object
    mdb.register_interface = function() end
    c_factory.get_instance = function() return { create_object = function() end } end
    c_network_adapter.collection = { find = function() return { children = {} } end }
end

local function setup_sd_bus_mocks()
    local sd_bus = require 'sd_bus'
    local orig_match_rule = sd_bus.MatchRule
    local orig_property_changed = sd_bus.ObjMgrPropertiesChanged
    sd_bus.MatchRule = { signal = function() return {} end }
    sd_bus.ObjMgrPropertiesChanged = { name = 'test', interface = 'test' }
    return orig_match_rule, orig_property_changed
end

local function restore_sd_bus_mocks(orig_match_rule, orig_property_changed)
    local sd_bus = require 'sd_bus'
    sd_bus.MatchRule = orig_match_rule
    sd_bus.ObjMgrPropertiesChanged = orig_property_changed
end

function TestCardMgmt:test_init_os_power_state_success()
    local mock_device_obj = create_mock_device_obj(0)
    local captured_callback = nil
    local log_calls = {}

    -- 保存原始函数
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_log_notice = log.notice
    local orig_log_error = log.error
    local client = require 'network_adapter.client'
    local fructl = require 'infrastructure.fructl'
    local orig_client_callback = client.OnFruCtrlPropertiesChanged
    local orig_fructl_status = fructl.get_power_status

    -- 设置打桩
    local orig_match_rule, orig_property_changed = setup_sd_bus_mocks()
    setup_log_capture(log_calls)
    setup_basic_mocks()

    comm_fun.get_device_obj_by_path_interface = function() return mock_device_obj end
    client.OnFruCtrlPropertiesChanged = function(callback) captured_callback = callback end
    fructl.get_power_status = function() return 'ON' end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}

    mgr:init_obj('/test/path')

    lu.assertEquals(mock_device_obj.OSPowerState, 1)
    lu.assertNotNil(captured_callback)
    lu.assertNotEquals(#log_calls, 0)
    lu.assertNotNil(string.find(log_calls[#log_calls], 'os power state set to 1'))

    -- 还原
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    log.notice = orig_log_notice
    log.error = orig_log_error
    restore_sd_bus_mocks(orig_match_rule, orig_property_changed)
    client.OnFruCtrlPropertiesChanged = orig_client_callback
    fructl.get_power_status = orig_fructl_status
end

function TestCardMgmt:test_init_os_power_state_device_not_found()
    local log_calls = {}

    -- 保存原始函数
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_log_error = log.error
    local orig_log_notice = log.notice

    -- 设置打桩
    local orig_match_rule, orig_property_changed = setup_sd_bus_mocks()
    setup_log_capture(log_calls)
    setup_basic_mocks()

    -- 让init_os_power_state失败
    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.NETWORK_ADAPTER_DEVICE_INTERFACE then
            return nil -- init_os_power_state失败
        end
        return {}      -- 其他调用成功
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}

    mgr:init_obj('/test/path')

    lu.assertNotEquals(#log_calls, 0)
    lu.assertNotNil(string.find(log_calls[#log_calls], 'init_os_power_state failed'))

    -- 还原
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    log.error = orig_log_error
    log.notice = orig_log_notice
    restore_sd_bus_mocks(orig_match_rule, orig_property_changed)
end

function TestCardMgmt:test_init_os_power_state_power_change_callback()
    local mock_device_obj = create_mock_device_obj(0)
    local captured_callback = nil
    local log_calls = {}

    -- 保存原始函数
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_log_notice = log.notice
    local client = require 'network_adapter.client'
    local fructl = require 'infrastructure.fructl'
    local orig_client_callback = client.OnFruCtrlPropertiesChanged
    local orig_fructl_status = fructl.get_power_status

    -- 设置打桩
    local orig_match_rule, orig_property_changed = setup_sd_bus_mocks()
    setup_log_capture(log_calls)
    setup_basic_mocks()

    comm_fun.get_device_obj_by_path_interface = function() return mock_device_obj end
    client.OnFruCtrlPropertiesChanged = function(self, callback) captured_callback = callback end
    fructl.get_power_status = function() return 'OFF' end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}

    mgr:init_obj('/test/path')

    lu.assertEquals(mock_device_obj.OSPowerState, 0)
    lu.assertNotNil(captured_callback)

    -- 测试回调函数
    captured_callback({ PowerState = { value = function() return 'ON' end } })
    lu.assertEquals(mock_device_obj.OSPowerState, 1)
    lu.assertNotNil(string.find(log_calls[#log_calls], 'os power state changed to 1'))

    captured_callback({ PowerState = { value = function() return 'OFF' end } })
    lu.assertEquals(mock_device_obj.OSPowerState, 0)
    lu.assertNotNil(string.find(log_calls[#log_calls], 'os power state changed to 0'))

    -- 还原
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    log.notice = orig_log_notice
    restore_sd_bus_mocks(orig_match_rule, orig_property_changed)
    client.OnFruCtrlPropertiesChanged = orig_client_callback
    fructl.get_power_status = orig_fructl_status
end

function TestCardMgmt:test_init_os_power_state_initial_power_off()
    local mock_device_obj = create_mock_device_obj(1)
    local log_calls = {}

    -- 保存原始函数
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_log_notice = log.notice
    local client = require 'network_adapter.client'
    local fructl = require 'infrastructure.fructl'
    local orig_client_callback = client.OnFruCtrlPropertiesChanged
    local orig_fructl_status = fructl.get_power_status

    -- 设置打桩
    local orig_match_rule, orig_property_changed = setup_sd_bus_mocks()
    setup_log_capture(log_calls)
    setup_basic_mocks()

    comm_fun.get_device_obj_by_path_interface = function() return mock_device_obj end
    client.OnFruCtrlPropertiesChanged = function() end
    fructl.get_power_status = function() return 'OFF' end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}

    mgr:init_obj('/test/path')

    lu.assertEquals(mock_device_obj.OSPowerState, 0)
    lu.assertNotEquals(#log_calls, 0)
    lu.assertNotNil(string.find(log_calls[#log_calls], 'os power state set to 0'))

    -- 还原
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    log.notice = orig_log_notice
    restore_sd_bus_mocks(orig_match_rule, orig_property_changed)
    client.OnFruCtrlPropertiesChanged = orig_client_callback
    fructl.get_power_status = orig_fructl_status
end

function TestCardMgmt:test_get_device_name_success()
    local mock_device_obj = { DeviceName = 'test_device_name' }
    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_DEVICE_INTERFACE then
            return mock_device_obj
        end
        return {}
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    -- 测试通过，说明get_device_name成功执行了
    lu.assertTrue(true)
end

function TestCardMgmt:test_get_device_name_failed()
    local log_calls = {}

    -- 设置打桩
    setup_log_capture(log_calls)

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_DEVICE_INTERFACE then
            return nil -- 模拟获取失败
        end
        return {}      -- 其他接口返回正常对象
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    -- 验证错误日志
    local found_error = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'get_device_name failed to get pcie device obj') then
            found_error = true
            break
        end
    end
    lu.assertTrue(found_error, "Should log error when get_device_name fails")
end

function TestCardMgmt:test_get_component_by_name_with_retry_success()
    local found_component_log = false
    local health_update_log = false

    -- 保存原始函数
    local orig_log_notice = log.notice
    local client = require 'network_adapter.client'
    local orig_foreach_component = client.ForeachComponentObjects
    local orig_on_component_changed = client.OnComponentPropertiesChanged

    -- 直接mock log.notice来捕获日志
    log.notice = function(self, fmt, ...)
        local msg = string.format(fmt, ...)
        if string.find(msg, 'found matching component test_device_name') then
            found_component_log = true
        end
        if string.find(msg, 'Health updated from matching component') then
            health_update_log = true
        end
    end

    local mock_pcie_device_obj = { DeviceName = 'test_device_name' }
    local mock_card_device_obj = { Health = 'OK' }
    local mock_component_obj = { Name = 'test_device_name', Health = 'Critical' }

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_DEVICE_INTERFACE then
            return mock_pcie_device_obj
        elseif interface == comm_defs.PCIE_CARD_DEVICE_INTERFACE then
            return mock_card_device_obj
        end
        return {}
    end

    -- 重新设置client mock，注意使用冒号调用
    client.OnComponentPropertiesChanged = function(self, callback) end
    client.ForeachComponentObjects = function(self, callback)
        callback(mock_component_obj) -- 第一次调用就找到
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    -- 验证Health被更新
    lu.assertEquals(mock_card_device_obj.Health, 'Critical')

    -- 验证找到组件的日志
    lu.assertTrue(found_component_log, "Should log notice when component is found")

    -- 验证Health更新日志
    lu.assertTrue(health_update_log, "Should log health update from component")

    -- 恢复原始函数
    log.notice = orig_log_notice
    client.ForeachComponentObjects = orig_foreach_component
    client.OnComponentPropertiesChanged = orig_on_component_changed
end

function TestCardMgmt:test_get_component_by_name_with_retry_max_retries()
    local log_calls = {}

    -- 设置打桩
    setup_log_capture(log_calls)
    local client = require 'network_adapter.client'

    local sleep_count = 0
    local mock_pcie_device_obj = { DeviceName = 'test_device_name' }
    local mock_card_device_obj = { Health = 'OK' }

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_DEVICE_INTERFACE then
            return mock_pcie_device_obj
        elseif interface == comm_defs.PCIE_CARD_DEVICE_INTERFACE then
            return mock_card_device_obj
        end
        return {}
    end

    skynet.sleep = function(ms)
        sleep_count = sleep_count + 1
        lu.assertEquals(ms, 300) -- 验证睡眠时间
    end

    -- 不调用callback，模拟找不到组件
    client.ForeachComponentObjects = function(callback)
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    -- 验证重试了20次，睡眠了20次
    lu.assertEquals(sleep_count, 20)
end

function TestCardMgmt:test_get_component_by_path_and_name_success()
    local health_change_logged = false
    local orig_log_notice = log.notice

    -- 直接mock log.notice来捕获日志
    log.notice = function(self, fmt, ...)
        local msg = string.format(fmt, ...)
        if string.find(msg, 'Health changed to Warning for matching component test_device_name') then
            health_change_logged = true
        end
    end

    local mock_pcie_device_obj = { DeviceName = 'test_device_name' }
    local mock_card_device_obj = { Health = 'OK' }
    local mock_component_obj = { Name = 'test_device_name', Health = 'Warning' }

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_DEVICE_INTERFACE then
            return mock_pcie_device_obj
        elseif interface == comm_defs.PCIE_CARD_DEVICE_INTERFACE then
            return mock_card_device_obj
        end
        return {}
    end

    local captured_callback = nil
    local client = require 'network_adapter.client'
    -- 保存原始函数
    local orig_on_component_changed = client.OnComponentPropertiesChanged
    local orig_foreach_component = client.ForeachComponentObjects
    local orig_get_component_objects = client.GetComponentObjects

    client.OnComponentPropertiesChanged = function(self, callback)
        captured_callback = callback
    end
    -- 不找到组件，让get_component_by_name_with_retry返回nil
    client.ForeachComponentObjects = function(self, callback)
    end
    client.GetComponentObjects = function(self)
        return { ['/test/component/path'] = mock_component_obj }
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    lu.assertNotNil(captured_callback, "Should capture component properties change callback")
    lu.assertEquals(type(captured_callback), 'function', "Captured callback should be a function")

    -- 测试回调函数
    captured_callback(nil, '/test/component/path', nil)

    -- 验证Health被更新
    lu.assertEquals(mock_card_device_obj.Health, 'Warning')

    -- 验证日志
    lu.assertTrue(health_change_logged, "Should log health change notice")

    -- 恢复原始函数
    log.notice = orig_log_notice
    client.OnComponentPropertiesChanged = orig_on_component_changed
    client.ForeachComponentObjects = orig_foreach_component
    client.GetComponentObjects = orig_get_component_objects
end

function TestCardMgmt:test_get_component_by_path_and_name_no_match()
    local mock_pcie_device_obj = { DeviceName = 'test_device_name' }
    local mock_card_device_obj = { Health = 'OK' }
    local mock_component_obj = { Name = 'different_name', Health = 'Warning' }

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_DEVICE_INTERFACE then
            return mock_pcie_device_obj
        elseif interface == comm_defs.PCIE_CARD_DEVICE_INTERFACE then
            return mock_card_device_obj
        end
        return {}
    end

    local captured_callback = nil
    local client = require 'network_adapter.client'
    -- 保存原始函数
    local orig_on_component_changed = client.OnComponentPropertiesChanged
    local orig_foreach_component = client.ForeachComponentObjects
    local orig_get_component_objects = client.GetComponentObjects

    client.OnComponentPropertiesChanged = function(self, callback)
        captured_callback = callback
    end
    -- 不找到组件
    client.ForeachComponentObjects = function(self, callback)
    end
    client.GetComponentObjects = function(self)
        return { ['/test/component/path'] = mock_component_obj } -- 名称不匹配
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    lu.assertNotNil(captured_callback, "Should capture component properties change callback")
    lu.assertEquals(type(captured_callback), 'function', "Captured callback should be a function")

    -- 测试回调函数，名称不匹配时不应更新Health
    local original_health = mock_card_device_obj.Health
    captured_callback(nil, '/test/component/path', nil)

    -- 验证Health没有被更新
    lu.assertEquals(mock_card_device_obj.Health, original_health)

    -- 恢复原始函数
    client.OnComponentPropertiesChanged = orig_on_component_changed
    client.ForeachComponentObjects = orig_foreach_component
    client.GetComponentObjects = orig_get_component_objects
end

function TestCardMgmt:test_init_component_health_failed()
    local log_calls = {}

    -- 设置打桩
    setup_log_capture(log_calls)

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_CARD_DEVICE_INTERFACE then
            return nil -- 模拟init_component_health失败
        end
        return {}      -- 其他接口返回正常对象
    end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    -- 验证错误日志
    local found_error = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'init component health failed') then
            found_error = true
            break
        end
    end
    lu.assertTrue(found_error, "Should log error when init_component_health fails")
end

-- 测试真正的collect_log_by_smbus源代码执行路径
function TestCardMgmt:test_real_collect_log_by_smbus_execution()
    local method_called = false
    local log_calls = {}
    local signal_callback = nil
    
    -- 保存原始函数
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_get_object_name = comm_fun.get_object_name_by_device_path
    local orig_mdb_get_object = mdb.get_object
    local orig_mdb_register = mdb.register_interface
    local c_network_adapter = require 'device.class.network_adapter'
    local orig_create_mdb_object = c_network_adapter.create_mdb_object
    local orig_collection_find = c_network_adapter.collection.find
    local client = require 'network_adapter.client'
    local orig_on_component_changed = client.OnComponentPropertiesChanged
    local log_collector = require 'device.class.log_collector'
    local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
    local orig_create_dir = log_collector.create_dir
    local orig_log_notice = log.notice
    
    -- 设置日志捕获
    setup_log_capture(log_calls)
    log.notice = function(_, fmt, ...)
        table.insert(log_calls, string.format(fmt, ...))
    end
    
    -- 模拟D-Bus消息对象
    local mock_msg = {
        read = function(self, format)
            return comm_defs.NETWORK_ADAPTER_DEVICE_FAULT_STATUS_INTERFACE, {
                FaultState = { value = function() return 1 end }  -- 故障状态
            }
        end
    }
    
    -- 模拟设备对象和依赖
    local mock_log_device_obj = {
        call_method = function(self, method, signature, dir)
            method_called = true
            return true
        end
    }
    
    local mock_card_orm_obj = {
        children = {}, NetworkPortCount = 1, NodeId = 'test_node', ID = 'test_node',
        smbus_collect_status = comm_defs.LOG_DUMP_IDLE,
        smbus_has_collected = false
    }
    
    -- 模拟bus.match方法来捕获信号回调
    local mock_bus = create_mock_bus()
    mock_bus.match = function(self, signal_rule, callback)
        signal_callback = callback
        return {} -- 返回信号订阅对象
    end
    mock_bus.call = function() return { ['/test/path'] = { 'test_interface' } } end
    
    -- 设置打桩
    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
            return mock_log_device_obj
        end
        return {}
    end
    comm_fun.get_object_name_by_device_path = function() return 'test_obj' end
    mdb.get_object = function() return { get_property = function() return 0, 'test_device' end } end
    mdb.register_interface = function() end
    c_network_adapter.create_mdb_object = function() return mock_card_orm_obj end
    c_network_adapter.collection.find = function() return mock_card_orm_obj end
    client.OnComponentPropertiesChanged = function() end
    log_collector.get_smbus_log_dir = function() return '/test/smbus/dir' end
    log_collector.create_dir = function() return true end
    
    -- 执行init_obj创建对象并注册信号
    local mgr = card_mgmt
    mgr.bus = mock_bus
    mgr.objects = {}
    mgr.sig_slot = {}
    
    local result = pcall(function() mgr:init_obj('/test/path') end)
    
    -- 验证对象创建成功
    lu.assertTrue(result, "Should successfully create card object")
    lu.assertNotNil(signal_callback, "Should register property change signal")
    
    -- 现在触发真正的信号处理器，这会调用源代码中的collect_log_by_smbus
    if signal_callback then
        signal_callback(mock_msg)
    end
    
    -- 验证源代码被执行
    lu.assertTrue(method_called, "Should call CollectLogBySmbus method from source code")
    lu.assertTrue(mock_card_orm_obj.smbus_has_collected, "Should set smbus_has_collected flag")
    
    -- 验证日志
    local found_log = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'collect_log_by_smbus start') then
            found_log = true
            break
        end
    end
    lu.assertTrue(found_log, "Should log collect_log_by_smbus start message")
    
    -- 还原打桩
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    comm_fun.get_object_name_by_device_path = orig_get_object_name
    mdb.get_object = orig_mdb_get_object
    mdb.register_interface = orig_mdb_register
    c_network_adapter.create_mdb_object = orig_create_mdb_object
    c_network_adapter.collection.find = orig_collection_find
    client.OnComponentPropertiesChanged = orig_on_component_changed
    log_collector.get_smbus_log_dir = orig_get_smbus_log_dir
    log_collector.create_dir = orig_create_dir
    log.notice = orig_log_notice
end

-- 测试BMC重启信号触发collect_log_by_smbus源代码执行
function TestCardMgmt:test_bmc_reset_signal_triggers_source_code()
    local smbus_method_called = false
    local ncsi_method_called = false
    local log_calls = {}
    local bmc_callback = nil
    
    -- 保存原始函数
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_get_object_name = comm_fun.get_object_name_by_device_path
    local orig_mdb_get_object = mdb.get_object
    local orig_mdb_register = mdb.register_interface
    local c_network_adapter = require 'device.class.network_adapter'
    local orig_create_mdb_object = c_network_adapter.create_mdb_object
    local orig_collection_find = c_network_adapter.collection.find
    local client = require 'network_adapter.client'
    local orig_on_component_changed = client.OnComponentPropertiesChanged
    local log_collector = require 'device.class.log_collector'
    local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
    local orig_get_ncsi_log_dir = log_collector.get_ncsi_log_dir
    local orig_create_dir = log_collector.create_dir
    local orig_bmc_sig = log_collector.log_dump_reset_bmc_sig
    local orig_log_notice = log.notice
    
    -- 设置日志捕获
    setup_log_capture(log_calls)
    log.notice = function(_, fmt, ...)
        table.insert(log_calls, string.format(fmt, ...))
    end
    
    -- 模拟设备对象
    local mock_log_device_obj = {
        call_method = function(self, method, signature, dir)
            if method == 'CollectLogBySmbus' then
                smbus_method_called = true
            elseif method == 'CollectLogByNcsi' then
                ncsi_method_called = true
            end
            return true
        end
    }
    
    -- 关键：模拟card_orm_obj，让connect_signal能够捕获并执行callback
    local mock_card_orm_obj = {
        children = {}, NetworkPortCount = 1, NodeId = 'test_node', ID = 'test_node',
        smbus_collect_status = comm_defs.LOG_DUMP_IDLE,
        ncsi_collect_status = comm_defs.LOG_DUMP_IDLE,
        smbus_has_collected = false,
        connect_signal = function(self, signal, callback)
            -- 如果是BMC重启信号，保存callback
            if signal == log_collector.log_dump_reset_bmc_sig then
                bmc_callback = callback
            end
        end,
        next_tick = function(self, callback)
            -- 立即执行callback，模拟异步执行
            callback()
        end,
        wait_mpu_idle = function() return true end
    }
    
    local mock_bus = create_mock_bus()
    mock_bus.call = function() return { ['/test/path'] = { 'test_interface' } } end
    
    -- 设置打桩
    log_collector.log_dump_reset_bmc_sig = { name = 'bmc_reset' }
    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
            return mock_log_device_obj
        end
        return {}
    end
    comm_fun.get_object_name_by_device_path = function() return 'test_obj' end
    mdb.get_object = function() return { get_property = function() return 0, 'test_device' end } end
    mdb.register_interface = function() end
    c_network_adapter.create_mdb_object = function() return mock_card_orm_obj end
    c_network_adapter.collection.find = function() return mock_card_orm_obj end
    client.OnComponentPropertiesChanged = function() end
    log_collector.get_smbus_log_dir = function() return '/test/smbus/dir' end
    log_collector.get_ncsi_log_dir = function() return '/test/ncsi/dir' end
    log_collector.create_dir = function() return true end
    
    -- 第一步：通过init_obj调用listen_bmc_os_reset_signal注册信号
    local mgr = card_mgmt
    mgr.bus = mock_bus
    mgr.objects = {}
    mgr.sig_slot = {}
    
    local result = pcall(function() mgr:init_obj('/test/path') end)
    
    -- 验证对象创建和信号注册成功
    lu.assertTrue(result, "Should successfully create card object")
    lu.assertNotNil(bmc_callback, "Should register BMC reset signal callback")
    
    -- 第二步：触发BMC重启信号，这会执行第195-204行的源代码
    if bmc_callback then
        bmc_callback()  -- 执行源代码中第195行注册的callback
    end
    
    -- 验证源代码被真正执行
    lu.assertTrue(smbus_method_called, "Should call CollectLogBySmbus from source code")
    lu.assertTrue(ncsi_method_called, "Should call CollectLogByNcsi from source code")
    
    -- 验证日志（来自源代码第197和201行）
    local bmc_smbus_logged = false
    local bmc_ncsi_logged = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'receive bmc reset signal, collect log by smbus') then
            bmc_smbus_logged = true
        elseif string.find(msg, 'receive bmc reset signal, collect log by ncsi') then
            bmc_ncsi_logged = true
        end
    end
    lu.assertTrue(bmc_smbus_logged, "Should log BMC smbus message from source code")
    lu.assertTrue(bmc_ncsi_logged, "Should log BMC ncsi message from source code")
    
    -- 还原打桩
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    comm_fun.get_object_name_by_device_path = orig_get_object_name
    mdb.get_object = orig_mdb_get_object
    mdb.register_interface = orig_mdb_register
    c_network_adapter.create_mdb_object = orig_create_mdb_object
    c_network_adapter.collection.find = orig_collection_find
    client.OnComponentPropertiesChanged = orig_on_component_changed
    log_collector.get_smbus_log_dir = orig_get_smbus_log_dir
    log_collector.get_ncsi_log_dir = orig_get_ncsi_log_dir
    log_collector.create_dir = orig_create_dir
    log_collector.log_dump_reset_bmc_sig = orig_bmc_sig
    log.notice = orig_log_notice
end

-- 测试OS重启信号触发handle_reset_os源代码执行
function TestCardMgmt:test_os_reset_signal_triggers_source_code()
    local handle_reset_called = false
    local smbus_method_called = false
    local log_calls = {}
    local os_callback = nil
    local sleep_called = false
    
    -- 保存原始函数
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_get_object_name = comm_fun.get_object_name_by_device_path
    local orig_mdb_get_object = mdb.get_object
    local orig_mdb_register = mdb.register_interface
    local c_network_adapter = require 'device.class.network_adapter'
    local orig_create_mdb_object = c_network_adapter.create_mdb_object
    local orig_collection_find = c_network_adapter.collection.find
    local client = require 'network_adapter.client'
    local orig_on_component_changed = client.OnComponentPropertiesChanged
    local log_collector = require 'device.class.log_collector'
    local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
    local orig_get_ncsi_log_dir = log_collector.get_ncsi_log_dir
    local orig_create_dir = log_collector.create_dir
    local orig_smbios_sig = log_collector.log_dump_reset_smbios_sig
    local orig_log_notice = log.notice
    local skynet = require 'skynet'
    local orig_skynet_sleep = skynet.sleep
    
    -- 设置日志捕获
    setup_log_capture(log_calls)
    log.notice = function(_, fmt, ...)
        table.insert(log_calls, string.format(fmt, ...))
    end
    
    skynet.sleep = function(ms)
        sleep_called = true
        lu.assertEquals(ms, 12000)  -- 验证睡眠时间
    end
    
    -- 模拟设备对象
    local mock_reset_device_obj = {
        call_method = function(self, method, signature)
            if method == 'HandleOsResetSignal' then
                handle_reset_called = true
            end
            return true
        end
    }
    
    local mock_log_device_obj = {
        call_method = function(self, method, signature, dir)
            if method == 'CollectLogBySmbus' then
                smbus_method_called = true
            end
            return true
        end
    }
    
    -- 关键：模拟card_orm_obj，让connect_signal能够捕获并执行OS重启callback
    local mock_card_orm_obj = {
        children = {}, NetworkPortCount = 1, NodeId = 'test_node', ID = 'test_node',
        smbus_collect_status = comm_defs.LOG_DUMP_IDLE,
        ncsi_collect_status = comm_defs.LOG_DUMP_IDLE,
        connect_signal = function(self, signal, callback)
            -- 如果是OS重启信号，保存callback
            if signal == log_collector.log_dump_reset_smbios_sig then
                os_callback = callback
            end
        end,
        next_tick = function(self, callback)
            callback()  -- 立即执行callback
        end,
        wait_mpu_idle = function() return true end  -- 添加缺失的方法
    }
    
    local mock_bus = create_mock_bus()
    mock_bus.call = function() return { ['/test/path'] = { 'test_interface' } } end
    
    -- 设置打桩
    log_collector.log_dump_reset_smbios_sig = { name = 'smbios_reset' }
    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.NETWORK_ADAPTER_DEVICE_INTERFACE then
            return mock_reset_device_obj
        elseif interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
            return mock_log_device_obj
        end
        return {}
    end
    comm_fun.get_object_name_by_device_path = function() return 'test_obj' end
    mdb.get_object = function() return { get_property = function() return 0, 'test_device' end } end
    mdb.register_interface = function() end
    c_network_adapter.create_mdb_object = function() return mock_card_orm_obj end
    c_network_adapter.collection.find = function() return mock_card_orm_obj end
    client.OnComponentPropertiesChanged = function() end
    log_collector.get_smbus_log_dir = function() return '/test/smbus/dir' end
    log_collector.get_ncsi_log_dir = function() return '/test/ncsi/dir' end
    log_collector.create_dir = function() return true end
    
    -- 第一步：通过init_obj注册OS重启信号
    local mgr = card_mgmt
    mgr.bus = mock_bus
    mgr.objects = {}
    mgr.sig_slot = {}
    
    local result = pcall(function() mgr:init_obj('/test/path') end)
    
    -- 验证对象创建和信号注册成功
    lu.assertTrue(result, "Should successfully create card object")
    lu.assertNotNil(os_callback, "Should register OS reset signal callback")
    
    -- 第二步：触发OS重启信号，这会执行第206-216行的源代码
    if os_callback then
        os_callback()  -- 执行源代码中第206行注册的callback
    end
    
    -- 验证源代码被真正执行
    lu.assertTrue(handle_reset_called, "Should call HandleOsResetSignal from source code")
    lu.assertTrue(sleep_called, "Should call skynet.sleep from source code")
    lu.assertTrue(smbus_method_called, "Should call CollectLogBySmbus from source code")
    
    -- 验证日志（来自源代码第178和211行）
    local handle_reset_logged = false
    local os_smbus_logged = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'handle_reset_os, device_path is') then
            handle_reset_logged = true
        elseif string.find(msg, 'receive os reset signal, collect log by smbus') then
            os_smbus_logged = true
        end
    end
    lu.assertTrue(handle_reset_logged, "Should log handle_reset_os message from source code")
    lu.assertTrue(os_smbus_logged, "Should log OS smbus message from source code")
    
    -- 还原打桩
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    comm_fun.get_object_name_by_device_path = orig_get_object_name
    mdb.get_object = orig_mdb_get_object
    mdb.register_interface = orig_mdb_register
    c_network_adapter.create_mdb_object = orig_create_mdb_object
    c_network_adapter.collection.find = orig_collection_find
    client.OnComponentPropertiesChanged = orig_on_component_changed
    log_collector.get_smbus_log_dir = orig_get_smbus_log_dir
    log_collector.get_ncsi_log_dir = orig_get_ncsi_log_dir
    log_collector.create_dir = orig_create_dir
    log_collector.log_dump_reset_smbios_sig = orig_smbios_sig
    log.notice = orig_log_notice
    skynet.sleep = orig_skynet_sleep
end

-- 通用异常分支测试框架 - get_log_by_smbus
function TestCardMgmt:test_get_log_by_smbus_exception_branches()
    local test_cases = {
        {
            name = "device_obj_nil",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return nil
                    end
                    return {}
                end
                return { orig_get_device_obj = orig_get_device_obj }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
            end,
            expected_error = "get_log_by_smbus failed, device_obj is nil"
        },
        {
            name = "log_dir_nil", 
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
                
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return {}
                    end
                    return {}
                end
                log_collector.get_smbus_log_dir = function() return nil end
                
                return { 
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_smbus_log_dir = orig_get_smbus_log_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_smbus_log_dir = saved.orig_get_smbus_log_dir
            end,
            expected_error = "get_log_by_smbus failed, log_dir is nil"
        },
        {
            name = "create_dir_failed",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
                local orig_create_dir = log_collector.create_dir
                
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return {}
                    end
                    return {}
                end
                log_collector.get_smbus_log_dir = function() return '/test/dir' end
                log_collector.create_dir = function() return false end
                
                return {
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_smbus_log_dir = orig_get_smbus_log_dir,
                    orig_create_dir = orig_create_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_smbus_log_dir = saved.orig_get_smbus_log_dir
                log_collector.create_dir = saved.orig_create_dir
            end,
            expected_error = "get_log_by_smbus failed, create log_dir failed"
        },
        {
            name = "call_method_exception",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
                local orig_create_dir = log_collector.create_dir
                
                local mock_device_obj = {
                    call_method = function() error("test exception") end
                }
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return mock_device_obj
                    end
                    return {}
                end
                log_collector.get_smbus_log_dir = function() return '/test/dir' end
                log_collector.create_dir = function() return true end
                
                return {
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_smbus_log_dir = orig_get_smbus_log_dir,
                    orig_create_dir = orig_create_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_smbus_log_dir = saved.orig_get_smbus_log_dir
                log_collector.create_dir = saved.orig_create_dir
            end,
            expected_error = "get_log_by_smbus failed, err is"
        },
        {
            name = "call_method_returns_false",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
                local orig_create_dir = log_collector.create_dir
                
                local mock_device_obj = {
                    call_method = function() return false end
                }
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return mock_device_obj
                    end
                    return {}
                end
                log_collector.get_smbus_log_dir = function() return '/test/dir' end
                log_collector.create_dir = function() return true end
                
                return {
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_smbus_log_dir = orig_get_smbus_log_dir,
                    orig_create_dir = orig_create_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_smbus_log_dir = saved.orig_get_smbus_log_dir
                log_collector.create_dir = saved.orig_create_dir
            end,
            expected_error = "get_log_by_smbus failed, device_path is"
        }
    }

    for _, test_case in ipairs(test_cases) do
        local log_calls = {}
        setup_log_capture(log_calls)
        local signal_callback = nil
        
        -- 基础打桩设置
        local orig_get_object_name = comm_fun.get_object_name_by_device_path
        local orig_mdb_get_object = mdb.get_object
        local orig_mdb_register = mdb.register_interface
        local c_network_adapter = require 'device.class.network_adapter'
        local orig_create_mdb_object = c_network_adapter.create_mdb_object
        local orig_collection_find = c_network_adapter.collection.find
        local client = require 'network_adapter.client'
        local orig_on_component_changed = client.OnComponentPropertiesChanged
        
        local mock_card_orm_obj = {
            children = {}, NetworkPortCount = 1, NodeId = 'test_node', ID = 'test_node',
            smbus_collect_status = comm_defs.LOG_DUMP_IDLE,
            smbus_has_collected = false
        }
        
        -- 模拟bus.match方法来捕获信号回调
        local mock_bus = create_mock_bus()
        mock_bus.match = function(self, signal_rule, callback)
            signal_callback = callback
            return {}
        end
        mock_bus.call = function() return { ['/test/path'] = { 'test_interface' } } end
        
        -- 基础打桩
        comm_fun.get_device_obj_by_path_interface = function() return {} end
        comm_fun.get_object_name_by_device_path = function() return 'test_obj' end
        mdb.get_object = function() return { get_property = function() return 0, 'test_device' end } end
        mdb.register_interface = function() end
        c_network_adapter.create_mdb_object = function() return mock_card_orm_obj end
        c_network_adapter.collection.find = function() return mock_card_orm_obj end
        client.OnComponentPropertiesChanged = function() end
        
        -- 执行init_obj创建对象并注册信号
        local mgr = card_mgmt
        mgr.bus = mock_bus
        mgr.objects = {}
        mgr.sig_slot = {}
        
        local result = pcall(function() mgr:init_obj('/test/path') end)
        lu.assertTrue(result, "Should successfully create card object for " .. test_case.name)
        lu.assertNotNil(signal_callback, "Should register property change signal for " .. test_case.name)
        
        -- 在callback执行前设置测试特定的异常条件
        local saved = test_case.setup()
        
        -- 模拟D-Bus消息对象并触发回调
        local mock_msg = {
            read = function(self, format)
                return comm_defs.NETWORK_ADAPTER_DEVICE_FAULT_STATUS_INTERFACE, {
                    FaultState = { value = function() return 1 end }
                }
            end
        }
        
        -- 触发信号处理器，这时会使用刚设置的异常条件
        if signal_callback then
            signal_callback(mock_msg)
        end
        
        -- 验证预期的错误日志
        local found_error = false
        for _, msg in ipairs(log_calls) do
            if string.find(msg, test_case.expected_error) then
                found_error = true
                break
            end
        end
        lu.assertTrue(found_error, "Should log expected error for " .. test_case.name .. ": " .. test_case.expected_error)
        
        -- 清理测试特定的打桩
        test_case.teardown(saved)
        
        -- 还原基础打桩
        comm_fun.get_object_name_by_device_path = orig_get_object_name
        mdb.get_object = orig_mdb_get_object
        mdb.register_interface = orig_mdb_register
        c_network_adapter.create_mdb_object = orig_create_mdb_object
        c_network_adapter.collection.find = orig_collection_find
        client.OnComponentPropertiesChanged = orig_on_component_changed
    end
end

-- 通用异常分支测试框架 - get_log_by_ncsi
function TestCardMgmt:test_get_log_by_ncsi_exception_branches()
    local test_cases = {
        {
            name = "device_obj_nil",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return nil
                    end
                    return {}
                end
                return { orig_get_device_obj = orig_get_device_obj }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
            end,
            expected_error = "get_log_by_ncsi failed, device_obj is nil"
        },
        {
            name = "mpu_not_available",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return {}
                    end
                    return {}
                end
                return { orig_get_device_obj = orig_get_device_obj }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
            end,
            expected_error = "mpu is not available, device_path is"
        },
        {
            name = "log_dir_nil",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_ncsi_log_dir = log_collector.get_ncsi_log_dir
                
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return {}
                    end
                    return {}
                end
                log_collector.get_ncsi_log_dir = function() return nil end
                
                return {
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_ncsi_log_dir = orig_get_ncsi_log_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_ncsi_log_dir = saved.orig_get_ncsi_log_dir
            end,
            expected_error = "get_log_by_ncsi failed, log_dir is nil"
        },
        {
            name = "create_dir_failed",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_ncsi_log_dir = log_collector.get_ncsi_log_dir
                local orig_create_dir = log_collector.create_dir
                
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return {}
                    end
                    return {}
                end
                log_collector.get_ncsi_log_dir = function() return '/test/dir' end
                log_collector.create_dir = function() return false end
                
                return {
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_ncsi_log_dir = orig_get_ncsi_log_dir,
                    orig_create_dir = orig_create_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_ncsi_log_dir = saved.orig_get_ncsi_log_dir
                log_collector.create_dir = saved.orig_create_dir
            end,
            expected_error = "get_log_by_ncsi failed, create log_dir failed"
        },
        {
            name = "call_method_exception",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_ncsi_log_dir = log_collector.get_ncsi_log_dir
                local orig_create_dir = log_collector.create_dir
                
                local mock_device_obj = {
                    call_method = function() error("test exception") end
                }
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return mock_device_obj
                    end
                    return {}
                end
                log_collector.get_ncsi_log_dir = function() return '/test/dir' end
                log_collector.create_dir = function() return true end
                
                return {
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_ncsi_log_dir = orig_get_ncsi_log_dir,
                    orig_create_dir = orig_create_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_ncsi_log_dir = saved.orig_get_ncsi_log_dir
                log_collector.create_dir = saved.orig_create_dir
            end,
            expected_error = "get_log_by_ncsi failed, err is"
        },
        {
            name = "call_method_returns_false",
            setup = function()
                local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
                local log_collector = require 'device.class.log_collector'
                local orig_get_ncsi_log_dir = log_collector.get_ncsi_log_dir
                local orig_create_dir = log_collector.create_dir
                
                local mock_device_obj = {
                    call_method = function() return false end
                }
                comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
                    if interface == comm_defs.NETWORK_ADAPTER_DEVICE_LOG_COLLECTION_INTERFACE then
                        return mock_device_obj
                    end
                    return {}
                end
                log_collector.get_ncsi_log_dir = function() return '/test/dir' end
                log_collector.create_dir = function() return true end
                
                return {
                    orig_get_device_obj = orig_get_device_obj,
                    orig_get_ncsi_log_dir = orig_get_ncsi_log_dir,
                    orig_create_dir = orig_create_dir
                }
            end,
            teardown = function(saved)
                comm_fun.get_device_obj_by_path_interface = saved.orig_get_device_obj
                local log_collector = require 'device.class.log_collector'
                log_collector.get_ncsi_log_dir = saved.orig_get_ncsi_log_dir
                log_collector.create_dir = saved.orig_create_dir
            end,
            expected_error = "get_log_by_ncsi failed, device_path is"
        }
    }

    for _, test_case in ipairs(test_cases) do
        local log_calls = {}
        setup_log_capture(log_calls)
        local bmc_callback = nil
        
        -- 基础打桩设置
        local orig_get_object_name = comm_fun.get_object_name_by_device_path
        local orig_mdb_get_object = mdb.get_object
        local orig_mdb_register = mdb.register_interface
        local c_network_adapter = require 'device.class.network_adapter'
        local orig_create_mdb_object = c_network_adapter.create_mdb_object
        local orig_collection_find = c_network_adapter.collection.find
        local client = require 'network_adapter.client'
        local orig_on_component_changed = client.OnComponentPropertiesChanged
        local log_collector = require 'device.class.log_collector'
        local orig_get_smbus_log_dir = log_collector.get_smbus_log_dir
        local orig_get_ncsi_log_dir = log_collector.get_ncsi_log_dir
        local orig_create_dir = log_collector.create_dir
        local orig_bmc_sig = log_collector.log_dump_reset_bmc_sig
        
        local mock_card_orm_obj = {
            children = {}, NetworkPortCount = 1, NodeId = 'test_node', ID = 'test_node',
            smbus_collect_status = comm_defs.LOG_DUMP_IDLE,
            ncsi_collect_status = comm_defs.LOG_DUMP_IDLE,
            connect_signal = function(self, signal, callback)
                if signal == log_collector.log_dump_reset_bmc_sig then
                    bmc_callback = callback
                end
            end,
            next_tick = function(self, callback) callback() end,
            wait_mpu_idle = function() 
                -- 根据测试用例返回不同的值
                if test_case.name == "mpu_not_available" then
                    return false
                else
                    return true
                end
            end
        }
        
        local mock_bus = create_mock_bus()
        mock_bus.call = function() return { ['/test/path'] = { 'test_interface' } } end
        
        -- 基础打桩
        log_collector.log_dump_reset_bmc_sig = { name = 'bmc_reset' }
        comm_fun.get_device_obj_by_path_interface = function() return {} end
        comm_fun.get_object_name_by_device_path = function() return 'test_obj' end
        mdb.get_object = function() return { get_property = function() return 0, 'test_device' end } end
        mdb.register_interface = function() end
        c_network_adapter.create_mdb_object = function() return mock_card_orm_obj end
        c_network_adapter.collection.find = function() return mock_card_orm_obj end
        client.OnComponentPropertiesChanged = function() end
        log_collector.get_smbus_log_dir = function() return '/test/smbus/dir' end
        log_collector.get_ncsi_log_dir = function() return '/test/ncsi/dir' end
        log_collector.create_dir = function() return true end
        
        -- 执行init_obj创建对象并注册信号
        local mgr = card_mgmt
        mgr.bus = mock_bus
        mgr.objects = {}
        mgr.sig_slot = {}
        
        local result = pcall(function() mgr:init_obj('/test/path') end)
        lu.assertTrue(result, "Should successfully create card object for " .. test_case.name)
        lu.assertNotNil(bmc_callback, "Should register BMC reset signal for " .. test_case.name)
        
        -- 在callback执行前设置测试特定的异常条件
        local saved = test_case.setup()
        
        -- 触发BMC重启信号，这会调用collect_log_by_ncsi -> get_log_by_ncsi
        if bmc_callback then
            bmc_callback()
        end
        
        -- 验证预期的错误日志
        local found_error = false
        for _, msg in ipairs(log_calls) do
            if string.find(msg, test_case.expected_error) then
                found_error = true
                break
            end
        end
        lu.assertTrue(found_error, "Should log expected error for " .. test_case.name .. ": " .. test_case.expected_error)
        
        -- 清理测试特定的打桩
        test_case.teardown(saved)
        
        -- 还原基础打桩
        comm_fun.get_object_name_by_device_path = orig_get_object_name
        mdb.get_object = orig_mdb_get_object
        mdb.register_interface = orig_mdb_register
        c_network_adapter.create_mdb_object = orig_create_mdb_object
        c_network_adapter.collection.find = orig_collection_find
        client.OnComponentPropertiesChanged = orig_on_component_changed
        log_collector.get_smbus_log_dir = orig_get_smbus_log_dir
        log_collector.get_ncsi_log_dir = orig_get_ncsi_log_dir
        log_collector.create_dir = orig_create_dir
        log_collector.log_dump_reset_bmc_sig = orig_bmc_sig
    end
end

function TestCardMgmt:test_del_obj_remove_card()
    local mgr = card_mgmt
    mgr.objects = { ['/mock/card'] = { id = 1 } }

    local removed_obj
    local orig_del = c_network_adapter.collection.del_object
    c_network_adapter.collection.del_object = function(_, obj)
        removed_obj = obj
    end

    mgr:del_obj('/mock/card')

    lu.assertEquals(removed_obj.id, 1)
    lu.assertNil(mgr.objects['/mock/card'])

    c_network_adapter.collection.del_object = orig_del
end

return TestCardMgmt
